#include <iostream>
using namespace std;

class MyClassA
{
public:
    MyClassA()
    {
        my_a = 0;
        my_b = 0;
        cout << "no params..." << endl;
    }

    MyClassA(int a, int b)
    {
        my_a = a;
        my_b = b;
        cout << "two params ..." << endl;
    }

    MyClassA(const MyClassA& A)
    {
        my_a = A.my_a;
        my_b = A.my_b;
        cout << "copy ......" << endl;
    }

    ~MyClassA()
    {
        my_a = 0;
        my_b = 0;
        cout << "~MyClassA...." << endl;
    }

public:
    void PrintData()
    {
        cout << "my_a = " << my_a << " my_b = " << my_b << endl;
    }

private:
    int my_a;
    int my_b;
};

//第一种调用场景：用一个对象初始化另一个对象
void ClassTest1()
{
    MyClassA A1(1, 2); //调用有参构造函数

    MyClassA A2(A1); //括号法初始化调用拷贝构造函数
    A2.PrintData();

    MyClassA A3 = A1; //等号法初始化调用拷贝构造函数
    A3.PrintData();

    MyClassA A4; //调用了无参构造函数
    A4 = A1; //这里是等号赋值！！！和等号法初始化是两个概念
    //不调用构造函数，而是执行赋值操作，把A1的数据赋值给A4（默认=浅拷贝）
    A4.PrintData();


}

void ParaFuncTest1(MyClassA A)
{
    A.PrintData();
}
//&是引用运算符，表示参数是一个Myclass类的引用。
// 引用其实就是给原有对象起了一个别名，它与原对象占用同一块内存空间，
// 是C++给类新增加的特性。使用引用传参数可以不需调用复制构造函数，
// 从而节省空间和时间。在调用这个函数的时候只需将一个Myclass类的对象名放到括号里就可以。
void ParaFuncTest2(MyClassA& A)
{
    A.PrintData();
}

void ParaFuncTest3(MyClassA* A)
{
    A->PrintData();
}

//第二种调用场景：类定义对象做函数参数
void ClassTest2()
{
    MyClassA A1(1, 2);

    ParaFuncTest1(A1); //实参A1初始化形参A对象元素的时候，会调用拷贝构造函数
    ParaFuncTest2(A1); //不会调用拷贝构造函数，因为引用是变量别名，引用传递并没有出现新对象，
    //只是给现有对象起个别名进行传递
    ParaFuncTest3(&A1); //不会调用拷贝构造函数，因为传递的是对象A1的地址，并没有新的对象元素出现
}

MyClassA RetuFuncTest1()
{
    MyClassA A(1, 2);
    cout<< "RetuFuncTest1 inner:" <<&A << endl;
    return A; //创建一个匿名对象，并把匿名对象返回出去，此时调用拷贝构造函数
} //而A的生命周期到此结束，被析构

//MyClassA& RetuFuncTest2()
//MyClassA* RetuFuncTest3()
//{
// MyClassA A(1, 2);
// return &A; //A是局部变量，不能返回它的地址
//}

//第三种调用场景：函数返回类型为类定义的元素
void ClassTest3()
{

//    RetuFuncTest1(); //如果不用变量来接这个函数，那么会在 RetuFuncTest1() 函数的 return A;
//    std::cout << "RetuFuncTest1 ok......!\n" << endl;
    //语句处调用拷贝构造函数，并立即执行析构函数
    //这是因为，函数返回一个对象元素，而局部变量A的生命周期只在函数体内，
    //不能返回出来，所以会在return时创建一个匿名对象，主调函数种若没有
    //对象元素来接，那么会立即调用析构函数把匿名对象析构
    //RetuFuncTest2();
    //RetuFuncTest3();

    MyClassA A1 = RetuFuncTest1(); //这里不会再次调用拷贝构造函数，
    //因为编译器会把函数RetuFuncTest1()
    //返回出来的匿名对象直接转化为A1,因此匿名对象不会被析构，
    //在RetuFuncTest1()函数结束时只调用一次析构函数来析构局部变量A
    //匿名对象已经分配好了资源，并直接转化为A1，
    //所以A1初始化不需要再次调用拷贝构造函数
    A1.PrintData();




//    MyClassA A2; //调用无参构造函数
//    A2 = RetuFuncTest1(); //这是等号赋值操作！！！此时匿名对象也不会立即析构，而是在执行完这句话，
    //对A2赋值完之后，执行析构函数，析构匿名对象
    //（区别于匿名对象初始化A1，匿名对象转为A1，不会析构）
//    A2.PrintData();
} //生命周期结束，析构所有局部变量 A1(匿名对象) A2

int main()
{
//    ClassTest1(); //加断点单步调试来观察构造函数与析构函数的调用机制
//    ClassTest2();
//    ClassTest3();

    RetuFuncTest1();




    std::cout << "pause......!\n";
    system("pause");
    return 0;
}